//=============================================================================
// LazyParser.NET - C# Expression Parser for .NET 2.0 
//
// Copyright (c) 2008 Philippe Leybaert
//
// Permission is hereby granted, free of charge, to any person obtaining a copy 
// of this software and associated documentation files (the "Software"), to deal 
// in the Software without restriction, including without limitation the rights 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
// copies of the Software, and to permit persons to whom the Software is 
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in 
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//=============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

namespace Activa.LazyParser
{
    public delegate bool VariableResolver(string varName, out object var, out Type varType);

    public class ParserContext : IParserContext
    {
        public VariableResolver MissingVariableHandler;

        private Dictionary<string, object> _variables = new Dictionary<string, object>();
        private Dictionary<string, Type> _types = new Dictionary<string, Type>();

        private readonly IParserContext _parentContext;

        private bool _nullIsFalse;
        private bool _notNullIsTrue;
        private bool _notZeroIsTrue;
        private bool _emptyStringIsFalse;
        private bool _nonEmptyStringIsTrue;
        private bool _returnNullWhenNullReference;
        private bool _emptyCollectionIsFalse;
        private bool _caseSensitiveVariables = true;

        private AssignmentPermissions _assignmentPermissions = AssignmentPermissions.None;

        public ParserContext()
        {
        }

        protected ParserContext(ParserContext parentContext)
        {
            if (parentContext != null)
            {
                _parentContext = parentContext;

                _assignmentPermissions = parentContext._assignmentPermissions;
                _nullIsFalse = parentContext._nullIsFalse;
                _notNullIsTrue = parentContext._notNullIsTrue;
                _notZeroIsTrue = parentContext._notZeroIsTrue;
                _emptyStringIsFalse = parentContext._emptyStringIsFalse;
                _nonEmptyStringIsTrue = parentContext._nonEmptyStringIsTrue;
                _returnNullWhenNullReference = parentContext._returnNullWhenNullReference;
                _emptyCollectionIsFalse = parentContext._emptyCollectionIsFalse;
                _caseSensitiveVariables = parentContext._caseSensitiveVariables;
            }
        }

        public virtual IParserContext CreateLocal()
        {
            return new ParserContext(this);
        }

        public bool ReturnNullWhenNullReference
        {
            get { return _returnNullWhenNullReference; }
            set { _returnNullWhenNullReference = value; }
        }

        public bool NullIsFalse
        {
            get { return _nullIsFalse; }
            set { _nullIsFalse = value; }
        }

        public bool NotNullIsTrue
        {
            get { return _notNullIsTrue; }
            set { _notNullIsTrue = value; }
        }

        public bool EmptyStringIsFalse
        {
            get { return _emptyStringIsFalse; }
            set { _emptyStringIsFalse = value; }
        }

        public bool NonEmptyStringIsTrue
        {
            get { return _nonEmptyStringIsTrue; }
            set { _nonEmptyStringIsTrue = value; }
        }

        public bool CaseSensitiveVariables
        {
            get { return _caseSensitiveVariables; }
            set
            {
                if (value != _caseSensitiveVariables)
                {
                    if (value)
                    {
                        _variables = new Dictionary<string, object>();
                        _types = new Dictionary<string, Type>();
                    }
                    else
                    {
                        _variables = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
                        _types = new Dictionary<string, Type>(StringComparer.InvariantCultureIgnoreCase);
                        
                    }
                    _caseSensitiveVariables = value;
                }
            }
        }

        public AssignmentPermissions AssignmentPermissions
        {
            get { return _assignmentPermissions; }
            set { _assignmentPermissions = value; }
        }

        public bool EmptyCollectionIsFalse
        {
            get { return _emptyCollectionIsFalse; }
            set { _emptyCollectionIsFalse = value; }
        }

        public bool NotZeroIsTrue
        {
            get { return _notZeroIsTrue; }
            set { _notZeroIsTrue = value; }
        }


        public void SetLocal<T>(string name, T data)
        {
            SetLocal(name, data, typeof (T));
        }

        public void SetLocal(string name, IValueWithType data)
        {
            SetLocal(name, data.Value, data.Type);
        }

        public void SetLocal(string name, object data, Type type)
        {
            _variables[name] = data;
            _types[name] = type;
        }

        public void Set(string name, object data, Type type)
        {
            if (_parentContext != null && _parentContext.Exists(name))
                _parentContext.Set(name, data, type);

            SetLocal(name, data, type);
        }

        public void Set<T>(string name, T data)
        {
            Set(name, data, typeof (T));
        }

        public void Set(string name, IValueWithType data)
        {
            Set(name, data.Value, data.Type);
        }

        public void AddType(string name, Type type)
        {
            Set(name, ContextFactory.CreateType(type));
        }

        public void AddFunction(string name, Type type, string methodName)
        {
            Set(name, ContextFactory.CreateFunction(type, methodName));
        }

        public void AddFunction(string name, Type type, string methodName, object targetObject)
        {
            Set(name, ContextFactory.CreateFunction(type, methodName, targetObject));
        }

        public void AddFunction(string name, MethodInfo methodInfo)
        {
            Set(name, ContextFactory.CreateFunction(methodInfo));
        }

        public void AddFunction(string name, MethodInfo methodInfo, object targetObject)
        {
            Set(name, ContextFactory.CreateFunction(methodInfo, targetObject));
        }

        public virtual bool Exists(string varName)
        {
            if (_variables.ContainsKey(varName))
                return true;
            
            if (_parentContext == null || !_parentContext.Exists(varName))
                return false;

            return true;
        }

        public virtual bool Get(string varName, out object value, out Type type)
        {
            if (_variables.ContainsKey(varName))
            {
                value = _variables[varName];
                type = _types[varName];
            }
            else
            {
                if (_parentContext == null || !_parentContext.Get(varName, out value, out type))
                {
                    value = null;
                    type = typeof(object);

                    return false;
                }
            }

            if (type == typeof(object) && value != null)
                type = value.GetType();

            return true;
        }

        public bool ToBoolean(object value)
        {
            if (value is bool)
                return ((bool)value);

            if (_notZeroIsTrue)
            {
                if (value is int || value is uint || value is short || value is ushort || value is long || value is ulong || value is byte || value is sbyte)
                    return Convert.ToInt64(value) != 0;

                if (value is decimal)
                    return (decimal)value != 0m;

                if (value is float || value is double)
                    return Convert.ToDouble(value) == 0.0;
            }

            if (value is ICollection && _emptyCollectionIsFalse)
                return ((ICollection) value).Count > 0;

            if (value is IEnumerable && _emptyCollectionIsFalse)
            {
                IEnumerator enumerator = ((IEnumerable) value).GetEnumerator();

                if (enumerator.MoveNext())
                        return true;

                return false;
            }

            if (_nonEmptyStringIsTrue && (value is string) && ((string)value).Length > 0)
                return true;

            if (_emptyStringIsFalse && (value is string) && ((string)value).Length == 0)
                return false;

            if (_notNullIsTrue && value != null)
                return true;

            if (_nullIsFalse && value == null)
                return false;

            if (_parentContext != null)
                return _parentContext.ToBoolean(value);

            throw new NullReferenceException();
        }
    }
}